#ifndef __M_FMT_H_
#define __M_FMT_H_
#include <vector>
#include <string>
#include <memory>
#include "level.hpp"
#include "message.hpp"
//日志格式化模块，主要目的是通过不同的格式化项生成特定格式的日志输出
namespace mylog{
 /*  
   %d ⽇期
◦ %T 缩进
◦ %t 线程id
◦ %p ⽇志级别
◦ %c ⽇志器名称
◦ %f ⽂件名
◦ %l ⾏号
◦ %m ⽇志消息
◦ %n 换⾏
*/
//抽象基类，用于表示日志格式化的不同部分
class FormatItem{
  public:
  using ptr=std::shared_ptr<FormatItem>;//智能指针别名
  //纯虚函数，将日志信息格式化到输出流
  virtual void Format(std::ostream &out, const LogMsg &msg)=0;
  virtual ~FormatItem(){};
};

//表⽰要从LogMsg中取出有效⽇志数据
class MsgFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
   out<<msg._payload;

}
};

//表⽰要从LogMsg中取出⽇志等级
class LevelFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<LogLevel::toString(msg._level);

}
};


//表⽰要从LogMsg中取出⽇志器名称
class NameFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<msg._name;

}
};
//表⽰要从LogMsg中取出线程ID
class ThreadFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<msg._tid;

}
};
//表⽰要从LogMsg中取出时间戳并按照指定格式进⾏格式化
class TimeFormatItem:public FormatItem{
public:
TimeFormatItem(const std::string& fmt="%H:%M:%S"):_fmt(fmt){};
void Format(std::ostream &out, const LogMsg &msg) override{
struct tm t;
time_t stamp_time = msg._ctime;
//用于将时间戳转换为本地时间的函数
localtime_r(&stamp_time, &t);
//把 struct tm 结构体类型表示的时间按照指定的格式转换为字符串
char ret[128];
strftime(ret,127,_fmt.c_str(),&t);
out<<ret;
}
private:
std::string _fmt;
};
//表⽰要从LogMsg中取出源码所在⽂件名
class FileFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<msg._file;

}
};
//表⽰要从LogMsg中取出源码所在⾏号
class LineFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<msg._line;

}
};
//表⽰⼀个制表符缩进
class TabFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<"\t";

}
};
//表⽰⼀个换⾏
class NLineFormatItem:public FormatItem{
public:
void Format(std::ostream &out, const LogMsg &msg) override{
out<<"\n";

}
};
//表⽰⾮格式化的原始字符串
class OtherFormatItem:public FormatItem{
public:
OtherFormatItem(const std::string& str):_str(str){};
void Format(std::ostream &out, const LogMsg &msg) override{
out<<_str;

}
private:
std::string _str;
};

//用于处理日志格式的解析和格式化操作。
//它可以根据一个模式字符串（pattern）来生成一系列的 FormatItem，
//并通过 format() 方法将格式化后的结果输出。
    class Formatter{
      public:
      using ptr = std::shared_ptr<Formatter>;
      Formatter(const std::string& pattern="[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
      :_pattern(pattern){assert(parsePattern());};

       //通过一系列格式化项来格式化日志消息
       //格式化输出到指定的 ostream
       void format(std::ostream& out,LogMsg &msg){
              for(auto &it:_items){
                it->Format(out,msg);
              }
       }

       //格式化输出到指定的字符串
       std::string format(LogMsg &msg){
         std::stringstream ss;
         //把子项按照顺序导入结果字符串
         for(auto &it:_items){
          it->Format(ss,msg);
         }
         return ss.str();
       }
         
//对格式化字符串进行解析思路
    // 原始字符串判断：将不以%起始的字符串认定为原始字符串。
          //处理时，从字符串起始位置开始向后遍历，直到遇到%，此时标志着原始字符串结束。
    // %字符处理：当遇到%时，查看紧随其后的字符。
          //若紧随其后的字符还是%，则将其当作%字符处理；
          //若不是%，则表示紧随其后的字符是一个格式化字符。
    // 格式化字符子格式判断：
          //对于识别出的格式化字符，检查其之后是否存在{。若存在，则{之后、}之前的数据是该格式化字符的子格式。
    // 信息保存：
          //在整个处理过程中，需要将处理得到的信息（如不同部分对应的键值对信息，像key和val的值）保存下来，以便后续使用 。 
      bool parsePattern(){ 
        std::vector<std::pair<std::string,std::string>> fmt_order;
        size_t pos=0;
        std::string key,val;
        while(pos<_pattern.size()){
          //1.处理原始字符串--判断是否是%
          //是原始字符串
          if(_pattern[pos]!='%'){
            val.push_back(_pattern[pos++]);
            continue;
          }
          //2.不是原始字符串
          //2.1 %%的情况
          if(pos+1<_pattern.size()&&_pattern[pos+1]=='%'){
            //fmt_order.push_back({"","%"});
            val.push_back('%');
            pos+=2;
            continue;
          }
          if(val.empty()==false){
            fmt_order.push_back({"",val});
            val.clear();
          }
          //2.2 %+格式化字符情况
           pos+=1;  //指向格式化字符
            if(pos==_pattern.size()){
                   std::cout<<"%后没有格式化字符"<<std::endl; 
                   return false;           
    }
           key=_pattern[pos];
           pos+=1;//指向格式化字符后面的位置
           if(pos<_pattern.size()&&_pattern[pos]=='{'){
            pos+=1;  //指向子规则的起始位置
            while(pos<_pattern.size()&&_pattern[pos]!='}'){
              val.push_back(_pattern[pos++]);
            }
          
            if(pos==_pattern.size()){
              std::cout<<"子规则格式错误"<<std::endl;
              return false;
            }
            pos+=1;//指向}后的一个位置
           }
           
           fmt_order.push_back({key,val});
           key.clear();
           val.clear();
        
        }
         //根据解析得到的数据初始化格式化子项数组成员
        for(auto &it:fmt_order){
          _items.push_back(createItem(it.first,it.second));
          
         }
         return true;
      }

      //根据传入的格式化字符（例如 %m，%d 等）创建对应的 FormatItem 对象。
      FormatItem::ptr createItem(const std::string& key,const std::string& val){
        if (key == "m") {return FormatItem::ptr(new MsgFormatItem());}
        if (key == "p") {return FormatItem::ptr(new LevelFormatItem());}
         if (key == "c") {return FormatItem::ptr(new NameFormatItem());}
         if (key == "t") {return FormatItem::ptr(new ThreadFormatItem());}
         if (key == "n") {return FormatItem::ptr(new NLineFormatItem());}
         if (key == "d") {return FormatItem::ptr(new TimeFormatItem(val));}
         if (key == "f") {return FormatItem::ptr(new FileFormatItem());}
         if (key == "l") {return FormatItem::ptr(new LineFormatItem());}
         if (key == "T") {return FormatItem::ptr(new TabFormatItem());}
         if (key == "") {return FormatItem::ptr(new OtherFormatItem(val));}
         std::cout<<"没有对应的格式化字符"<<std::endl;
         abort();
      }
       private:
       //格式字符串
        std::string _pattern;
        //日志消息子类序列
      std::vector<FormatItem::ptr> _items;
    };

}
#endif